package com.fs.alert.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fs.alert.domain.dto.DispatchRequestDTO;
import com.fs.alert.domain.po.Alert;
import com.fs.alert.domain.po.AlertRobot;
import com.fs.api.enumeration.AlertStatus;
import com.fs.api.enumeration.AlertType;
import com.fs.api.enumeration.EmergencyLevel;
import com.fs.alert.mapper.AlertMapper;
import com.fs.alert.mapper.AlertRobotMapper;
import com.fs.alert.service.IAlertGeneratorService;
import com.fs.alert.socketio.service.SocketIOOperations;
import com.fs.alert.utils.AlertDescriptionGenerator;
import com.fs.api.client.RobotClient;
import com.fs.api.enumeration.RobotStatus;
import com.fs.api.enumeration.WorkStatus;
import com.fs.common.domain.Result;
import com.fs.common.exception.BusinessException;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.IntStream;

import static com.fs.common.enumeration.BusinessExceptionEnum.*;


// 在警情生成服务中使用Socket.IO
@Service
@Slf4j
@RequiredArgsConstructor
public class AlertGeneratorServiceImpl implements IAlertGeneratorService {
    private final AlertMapper alertMapper;
    private final AlertRobotMapper alertRobotMapper;

    private final SocketIOOperations socketIOOperations;

    private final RobotClient robotClient;

    @Override
    @Scheduled(cron = "0 0 */8 * * *")
    public void generateHourlyAlerts() {
        for (int i = 0; i < 2; i++) {
            Alert alert = generateRandomAlert();
            int rows = alertMapper.insert(alert);
            log.info("新警情: {}, ID: {}, 插入行数: {}", alert.getAlertNo(), alert.getId(), rows);
            socketIOOperations.sendEvent("newAlert", alert);
        }
    }

    // 每秒向前端发送123
    @Override
//    @Scheduled(cron = "0 * * * * *")
    public void sendRandomNumber() {
        socketIOOperations.sendEvent("test", LocalDateTime.now());
    }

    private Alert generateRandomAlert() {
        Random random = new Random();
        String alertNo = "ALERT-" +
                LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHH")) +
                String.format("%04d", random.nextInt(10000));

        LocalDateTime alertTime = LocalDateTime.now()
                .withMinute(random.nextInt(60))
                .withSecond(random.nextInt(60));

        String[] locations = IntStream.rangeClosed('A', 'Z')
                .mapToObj(ch -> String.valueOf((char) ch) + "区")
                .toArray(String[]::new);

        AlertType alertType = AlertType.values()[random.nextInt(AlertType.values().length)];
        EmergencyLevel emergencyLevel = EmergencyLevel.values()[random.nextInt(EmergencyLevel.values().length)];

        return Alert.builder()
                .alertNo(alertNo)
                .alertType(alertType)
                .location(locations[random.nextInt(locations.length)])
                .alertTime(alertTime)
                .createdTime(alertTime)
                .updatedTime(alertTime)
                .status(AlertStatus.PENDING)
                .emergencyLevel(emergencyLevel)
                .environmentDesc(AlertDescriptionGenerator.generateEnvironmentDesc(alertType, emergencyLevel))
                .dangerousGoods(AlertDescriptionGenerator.generateDangerousGoods(alertType, emergencyLevel))
                .incidentDesc(AlertDescriptionGenerator.generateIncidentDesc(alertType, emergencyLevel))
                .build();
    }

    @Override
    @GlobalTransactional
    public void dispatchTask(DispatchRequestDTO request) {
        Long alertId = request.getAlertId();
        Long robotId = request.getRobotId();

        // 更改状态
        Alert alert = alertMapper.selectById(alertId);
        if (alert.getStatus() != AlertStatus.PENDING) {
            throw new BusinessException(ALERT_ALREADY_ASSIGNED);
        }
        alert.setStatus(AlertStatus.PROCESSING);
        alertMapper.updateById(alert);

        // 更改对应机器人状态
        Result r1 = robotClient.updateRobotStatus(robotId, RobotStatus.WORKING.getValue());
        Result r2 = robotClient.updateRobotLocation(robotId, alert.getLocation());


        if (r1.getCode() != 200 || r2.getCode() != 200) {
            throw new BusinessException(ASSIGN_FAILED);
        }

        // 保存到数据库
        saveDispatch(alertId, robotId);

        log.info("任务发布");
    }

    @Override
    public void completeTask(Long alertId) {
        // 获取机器人ID
        Long robotId = alertRobotMapper.selectOne(
            new LambdaQueryWrapper<AlertRobot>().eq(AlertRobot::getAlertId, alertId)
        ).getRobotId();

        // 更改状态
        Alert alert = alertMapper.selectById(alertId);
        if (alert.getStatus() != AlertStatus.PROCESSING) {
            throw new BusinessException(ALERT_STATE_ERROR);
        }
        alert.setStatus(AlertStatus.PROCESSED);
        alertMapper.updateById(alert);

        // 更改对应机器人状态
        Result r1 = robotClient.updateRobotStatus(robotId, RobotStatus.IDLE.getValue());
        // 地区从仓库A区-F区
        String location = "仓库" + (char)('A' + (int)(Math.random() * 6)) + "区";

        Result r2 = robotClient.updateRobotLocation(robotId, location);
        // 30到40随机数
        Result r3 = robotClient.chargeRobot(robotId, (int)(30 + Math.random() * 10));

        if (r1.getCode() != 200 || r2.getCode() != 200 || r3.getCode() != 200) {
            throw new BusinessException(ROBOT_UPDATE_STATUS_FAILED);
        }

        log.info("任务完成");
    }

    private void saveDispatch(Long alertId, Long robotId) {
        // 保存 alert_robot
        AlertRobot alertRobot = AlertRobot.builder()
                .alertId(alertId)
                .robotId(robotId)
                .createdTime(LocalDateTime.now())
                .build();
        alertRobotMapper.insert(alertRobot);

        log.info("Saved dispatch: alertId={}, robotId={}", alertId, robotId);
    }
}
